#pragma once
#include "Property.h"

class PropertySet
{
public:
	PropertySet(void)
	{

	}

	void create(int property_num)
	{
		m_propterties.clear();

		if (0 == property_num)
			return;

		m_propterties.resize(property_num);
		m_propterties_mask.resize(property_num,0);
	}

	void PropertySet::setValue(int property_id, uint32 value)
	{
		ASSERT(property_id < m_propterties.size() );

		m_propterties[property_id].setValue(value);
		setValueType(property_id,PT_UINT32);
	}

	void PropertySet::setValue(int property_id, float value)
	{
		ASSERT(property_id < m_propterties.size() );

		m_propterties[property_id].setValue(value);
		setValueType(property_id,PT_FLOAT);
	}

	void PropertySet::setValue(int property_id, const char* value)
	{
		ASSERT(property_id < m_propterties.size() );

		m_propterties[property_id].setValue(value);
		setValueType(property_id,PT_STRING);
	}


	uint32 getUint32Value(int property_id) const
	{
		ASSERT(property_id < m_propterties.size() );
		return m_propterties[property_id].getUint32Value();
	}

	float getFloatValue(int property_id) const
	{
		ASSERT(property_id < m_propterties.size() );
		return m_propterties[property_id].getFloatValue();
	}

	const char* getStringValue(int property_id) const
	{
		ASSERT(property_id < m_propterties.size() );
		return m_propterties[property_id].getStringValue();
	}

	void getValue(int property_id, uint32& v) const
	{
		ASSERT(property_id < m_propterties.size() );

		m_propterties[property_id].getValue(&v);
	}


	void getValue(int property_id, float& v) const
	{
		ASSERT(property_id < m_propterties.size() );

		m_propterties[property_id].getValue(&v);
	}

	void getValue(int property_id, const char*& v) const
	{
		ASSERT(property_id < m_propterties.size() );

		m_propterties[property_id].getValue(&v);
	}

	void resetUpdatedMask()
	{
		std::vector<uint8>::iterator it = m_propterties_mask.begin();
		while(it != m_propterties_mask.end())
		{
			*it &= 0xF0;
			++it;
		}
	}

	template<typename T>
	void visitAllProperties(T& visitor);

	template<typename T>
	void visitUpdatedProperties(T& visitor);

	typedef std::vector<Property>::iterator iterator;

	iterator begin()
	{
		return m_propterties.begin();
	}

	iterator end()
	{
		return m_propterties.end();
	}

	bool isPropertyUpdate(int property_id)
	{
		return  (m_propterties_mask[property_id] & 0x01);

	}

protected:
	void setPropertyUpdateMask( int property_id, const Property& p )
	{
		if (! isPropertyUpdate(property_id) )
		{
			m_propterties_mask[property_id] &= 0xF1;
			onPropertyUpdated(property_id, p);
		}
	}

	void setValueType(int property_id, ProtertyType type)
	{
		m_propterties_mask[property_id] &= (0x0F | type << 4);
	}

	ProtertyType getValueType(int property_id)
	{
		return ProtertyType(m_propterties_mask[property_id] & 0xF0 >> 4);
	}

	virtual void onPropertyUpdated(int property_id, const Property& p)
	{

	}

private:
	std::vector<Property> m_propterties;
	std::vector<uint8> m_propterties_mask;
};


template<typename T>
void PropertySet::visitAllProperties(T& visitor)
{
	ProtertyType t;
	for (size_t i=0; i<m_propterties.size(); ++i)
	{
		t = getValueType(i);
		if (t == PT_UINT32)
		{
			if (!visitor(i,getUint32Value(i)))
				return;
		}
		else if (t == PT_FLOAT)
		{
			if (!visitor(i,getFloatValue(i)))
				return;

		}
		else if (t == PT_STRING)
		{
			if (!visitor(i,getStringValue(i)))
				return;

		}
		else
		{
			ASSERT(false);
			return;
		}
	}
}

template<typename T>
void PropertySet::visitUpdatedProperties(T& visitor)
{
	ProtertyType t;
	for (size_t i=0; i<m_propterties.size(); ++i)
	{
		if (isPropertyUpdate(i))
		{
			t = getValueType(i);
			if (t == PT_UINT32)
			{
				if (!visitor(i,getUint32Value(i)))
					return;
			}
			else if (t == PT_FLOAT)
			{
				if (!visitor(i,getFloatValue(i)))
					return;

			}
			else if (t == PT_STRING)
			{
				if (!visitor(i,getStringValue(i)))
					return;

			}
			else
			{
				ASSERT(false);
				return;
			}
		}
	}
}